#include<iostream>
#include<string>
#include<filesystem>
#include<fstream>
#include<memory>
#include<unistd.h>
#include<cstdio>
#include"mymutex.hpp"
using namespace std;

namespace my_log
{
    //首先要实现刷新策略：我们采用继承多态
    enum class LogLevel//日志的等级
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    string L_level(LogLevel level)
    {
        switch(level)
        {
            case LogLevel::DEBUG:
            return "DEBUG";
            case LogLevel::INFO:
            return "INFO";
            case LogLevel::WARNING:
            return "WARNING";
            case LogLevel::ERROR:
            return "ERROR";
            case LogLevel::FATAL:
            return "FATAL";
            default:
            return nullptr;
        }
    }
    class LogStrategy//刷新策略的父类
    {
    public:
        LogStrategy()=default;//默认形成构造函数
        virtual void SyncLog(const string& messages)=0;
        ~LogStrategy()
        {}
    private:
    };
    class LogConsoleStrategy:public LogStrategy
    {
    public:
        LogConsoleStrategy()
        {
        }
        void SyncLog(const std::string &message) override
        {
            _mutex.lock();
            cout<<message<<endl;
            _mutex.unlock();
        }
        ~LogConsoleStrategy()
        {}
    private:
        my_mutex::mymutex _mutex;
    };
    string defaultpath="./mylog";
    string defaultname="log.txt";
    class LogFileStrategy:public LogStrategy
    {
    public:
        LogFileStrategy(string path=defaultpath,string name=defaultname)
        :_path(path)
        ,_name(name)
        {
            _mutex.lock();
            //判断_path是否存在
            if(std::filesystem::exists(_path))
            {
                return;//存在则返回
            }
            //不存在则创建_path
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch(const std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << '\n';
            }
            
            file=_path+(_path.back()=='/'?"":"/")+_name;//形成文件
            _mutex.unlock();
        }
        void SyncLog(const std::string& messages)override
        {
            _mutex.lock();
            std::ofstream out(file,std::ios::app);//追加方式写入
            if(!out.is_open())
            {
                return;
            }
            out<<messages<<"\r\n";
            out.close();
            _mutex.unlock();
        }
        ~LogFileStrategy()
        {
        }
    private:
        my_mutex::mymutex _mutex;
        string _path;
        string _name;
        string file;
    };
    class Logger
    {
    public:
        Logger()
        {
            ConsoleStrategy();//默认使用控制台刷新策略
        }
        void ConsoleStrategy()
        {
            _flush_way=std::make_unique<LogConsoleStrategy>();
        }
        void FileStrategy()
        {
            _flush_way=std::make_unique<LogFileStrategy>();
        }
        //内部类,形成一条完整的日志
        class LogMessage
        {
        public:
        LogMessage(string& time,LogLevel& level,string& src_name,int line,Logger &logger)
        :_time(time)
        ,_level(level)
        ,_pid(getpid())
        ,_src_name(src_name)
        ,_line(line)
        ,_logger(logger)
        {
            //形成日志的左半部分
            std::stringstream ss;
            ss<<'['<<time<<']'
            <<'['<<L_level(_level)<<']'
            <<'['<<_pid<<']'
            <<'['<<_src_name<<']'
            <<'['<<line<<']';
            _log=ss.str();
            _log+='-';
        }
        template<typename T>
        //重载<<
        LogMessage& operator<<(T messages)
        {
            std::stringstream ss;
            ss<<messages;
            _log+=ss.str();
            return *this;
        }
        ~LogMessage()
        {
            //析构函数调用,自动刷新日志内容
            _logger._flush_way->SyncLog(_log);
        }
        private:
            string _time;
            LogLevel _level;
            pid_t _pid;
            string _src_name;//文件名字
            int _line;//行号
            string _log;//合并好的日志信息
            Logger& _logger;//调用析构函数的时候进行刷新日志
        };
        LogMessage operator()(string time,LogLevel level,string name,int line)
        {
            return LogMessage(time,level,name,line,*this);
        }
    private:
        std::unique_ptr<LogStrategy> _flush_way;
    };
}